using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

using Atomic.Libraries;
using Atomic.Libraries.Mathematics;
using Atomic.Libraries.Plotting;
using Atomic.Libraries.Plotting.Functions;

namespace Atomic.Thermodynamics.Phonons
{
	[Serializable]
	public class PhononStateDensity : IPhononStateDensity
	{
		public PhononStateDensity(IEnumerable<IPhononStateDensityPoint> points, int phononStates)
		{
			Points = new ReadOnlyList<IPhononStateDensityPoint>(points);
			PhononStates = phononStates;
		}
				
		public void Plot()
		{
			IPlotFunction d = new LinearInterpolationFunction(Points.Select(p => new PlaneVector(p.Frequency, p.Density)));

			new Gnuplot()
				.SetXLabel("Frequency, THz")
				.SetYLabel("Density, THz^{-1}")
				.Plot(
					new FunctionPlot(f => d.Value(f * 1.0e12) * 1.0e12, Points.Min(p => p.Frequency) / 1.0e12, Points.Max(p => p.Frequency) / 1.0e12)
				)
				.Pause()
				.Run();
		}

		public static PhononStateDensity Read(FileInfo file, int phononStates)
		{
			return new PhononStateDensity(File.ReadLines(file.FullName).Select(s => { string[] cols = s.Split(' '); return new PhononStateDensityPoint(double.Parse(cols[0], CultureInfo.InvariantCulture), double.Parse(cols[1], CultureInfo.InvariantCulture)); }), phononStates);
		}

		public static PhononStateDensity ReadBinary(FileInfo file, int phononStates)
		{
			List<IPhononStateDensityPoint> points = new List<IPhononStateDensityPoint>();

			using (Stream stream = File.Open(file.FullName, FileMode.Open))
			{
				using (BinaryReader reader = new BinaryReader(stream))
				{
					// Appearently the way to check for end-of-file.
					while (reader.PeekChar() != -1)
					{
						points.Add(new PhononStateDensityPoint(reader.ReadDouble(), reader.ReadDouble()));
					}
				}
			}

			return new PhononStateDensity(points, phononStates);
		}

		public void Write(FileInfo file)
		{
			File.WriteAllLines(file.FullName, Points.Select(p => string.Format(CultureInfo.InvariantCulture, "{0:r} {1:r}", p.Frequency, p.Density)));
		}

		public void WriteBinary(FileInfo file)
		{
			using (Stream stream = File.Open(file.FullName, FileMode.Create))
			{
				using (BinaryWriter writer = new BinaryWriter(stream))
				{
					foreach (IPhononStateDensityPoint point in Points)
					{
						writer.Write(point.Frequency);
						writer.Write(point.Density);
					}
				}
			}
		}

		public ReadOnlyList<IPhononStateDensityPoint> Points
		{
			get;
			private set;
		}

		IEnumerable<IPhononStateDensityPoint> IPhononStateDensity.Points
		{
			get
			{
				return Points;
			}
		}

		/// <summary>
		/// The number of phonon states. This is three times the number of atoms per primitive cell.
		/// </summary>
		public int PhononStates
		{
			get;
			private set;
		}
	}
}
